/*
 *  Copyright 2010 Massimiliano Mazzarolo
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0
 *        
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 */
package grails.coherence

import com.tangosol.util.filter.EqualsFilter
import grails.coherence.FilterBuilder
import grails.coherence.extractor.GroovyPropertyValueExtractor;
import grails.test.GrailsUnitTestCase
import groovy.mock.interceptor.MockFor;

import com.tangosol.util.extractor.KeyExtractor;
import com.tangosol.util.extractor.PofExtractor;
import com.tangosol.util.extractor.ReflectionExtractor
import com.tangosol.util.filter.IsNullFilter
import com.tangosol.util.filter.AllFilter
import com.tangosol.util.filter.*
import com.tangosol.util.filter.NotFilter
import com.tangosol.util.Filter
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.filter.ContainsAllFilter
import com.tangosol.util.filter.ContainsAnyFilter
import com.tangosol.util.filter.BetweenFilter
import com.tangosol.util.filter.InFilter

class FilterBuilderTests extends GrailsUnitTestCase {
	
	private FilterBuilder underTest
	
	protected void setUp() {
		super.setUp()
		underTest = new FilterBuilder()
		
	}
	
	void testAllPredicate() {
		underTest.all()
		def filter = underTest.filter
		assert filter instanceof AlwaysFilter			
	}	
	
	void testEqualsPredicate() {
		
		underTest.equals("john", "doe")
		
		def filter = underTest.filter
		assertEqualsTo(filter, 'john', 'doe')
	}
	
	private def assertEqualsTo(Filter filter, name, value) {
		assert filter instanceof EqualsFilter
		assert filter.value == value
		assertMethodNameIs name, filter
	}
	
	void testIsNullPredicate() {
		underTest.isNull 'john'
		def filter = underTest.filter
		assert filter instanceof IsNullFilter
		assertMethodNameIs 'john', filter
	}
	
	void testAndPredicate() {
		underTest.and {
			isNull 'john'
			isNull 'doe'      
		}
		def filter = underTest.filter
		assert filter instanceof AllFilter
		
		assert filter.filters.length == 2
		
		assertMethodNameIs  'john', filter.filters[0]
		assertMethodNameIs  'doe', filter.filters[1]
	}
	
	
	void testOrPredicate() {
		underTest.or {
			isNull 'john'
			isNull 'doe'
		}
		def filter = underTest.filter
		assert filter instanceof AnyFilter
		
		assert filter.filters.length == 2
		
		assertMethodNameIs  'john', filter.filters[0]
		assertMethodNameIs  'doe', filter.filters[1]
	}
	
	void testNestedAndPredicate() {
		underTest.and {
			or {
				isNull 'john'
				isNull 'doe'
			}
			isNull 'a_name'
			isNull 'another_name'      
			
		}
		def filter = underTest.filter
		assert filter instanceof AllFilter
		
		assert filter.filters.length == 3
		
		assertMethodNameIs  'a_name', filter.filters[1]
		assertMethodNameIs  'another_name', filter.filters[2]
		
		def nestedFilter = filter.filters[0]
		assert nestedFilter instanceof AnyFilter
		
		assertMethodNameIs  'john', nestedFilter.filters[0]
		assertMethodNameIs  'doe', nestedFilter.filters[1]
		
	}
	
	void testNotPredicate() {
		underTest.not { equals 'john', 'doe' }
		
		def filter = underTest.filter
		assert filter instanceof NotFilter
		
		assertEqualsTo filter.filter, 'john', 'doe' 
	}
	
	void testNotNullPredicate() {
		underTest.notNull("john")
		
		def filter = underTest.filter
		assert filter instanceof IsNotNullFilter				 
	}
	
	void testContainsAllPredicate() {
		def values = ['a_name', 'another_name']
		underTest.containsAll "john", values
		def filter = underTest.filter
		assert filter instanceof ContainsAllFilter
		assertMethodNameIs "john", filter
		assert filter.value.containsAll(values)
	}
	
	void testContainsAnyPredicate() {
		def values = ['a_name', 'another_name']
		underTest.containsAny "john", values
		def filter = underTest.filter
		assert filter instanceof ContainsAnyFilter
		assertMethodNameIs "john", filter
		assert filter.value.containsAll(values)
	}
	
	void testBetweenPredicate() {
		
		underTest.between('john', [from:1, to:2])
		
		def filter = underTest.filter
		assert filter instanceof BetweenFilter
		
	}
	
	void testInPredicate() {
		def values = ['a_name', 'another_name']
		underTest.'in' "john", values
		def filter = underTest.filter
		assert filter instanceof InFilter
		assertMethodNameIs "john", filter
		assert filter.value.containsAll(values)
	}
	
	void testLimitPredicate() {		
		underTest.limit(1,10) { all() }
		def filter = underTest.filter
		assert filter instanceof LimitFilter
		assert filter.pageSize == 10
		assert filter.page == 1		
	}
	
	void testInKeySetPredicate() {
		underTest.inKeySet(['a', 'b']) { all() }
		def filter = underTest.filter
		assert filter instanceof InKeySetFilter		
	}
	
	void testLEPredicate() {
		underTest.le("getValue", 10)
		def filter = underTest.filter
		assert filter instanceof LessEqualsFilter
		assertMethodNameIs("getValue", filter)
	}
	
	void testLTPredicate() {
		underTest.lt("getValue", 10)
		def filter = underTest.filter
		assert filter instanceof LessFilter
		assertMethodNameIs("getValue", filter)
	}
	
	void testGEPredicate() {
		underTest.ge("getValue", 10)
		def filter = underTest.filter
		assert filter instanceof GreaterEqualsFilter	
		assertMethodNameIs("getValue", filter)
	}
	
	void testGTPredicate() {
		underTest.gt("getValue", 10)
		def filter = underTest.filter
		assert filter instanceof GreaterFilter	
		assertMethodNameIs("getValue", filter)
	}
	
	void testContainsPredicate() {
		underTest.contains("getValue", 10)
		def filter = underTest.filter
		assert filter instanceof ContainsFilter	
		assertMethodNameIs("getValue", filter)			
	}
	
	void testPofExtractor() {
		def extractor = underTest.pof(String, 1)
		assert extractor instanceof PofExtractor
		assert extractor.classExtracted == String		
	}
	
	void testMethodExtractor() {
		def extractor = underTest.method("toString")
		assert extractor instanceof ReflectionExtractor
		assert extractor.methodName == "toString"
	}
	
	void testKeyExtractionWithMethod() {
		def extractor = underTest.key("toString")
		assert extractor instanceof ReflectionExtractor
		assert extractor.methodName == "toString"		
	}
	
	void testKeyExtractionWithValueExtractor() {
		def mockContext = new MockFor(ValueExtractor)
		mockContext.demand.getClass() { ValueExtractor }
		def extractor = underTest.key(mockContext.proxyInstance())
		assert extractor instanceof KeyExtractor			
	}
	
	void testNoOthersTypeAreSupportedAsKeyArg() {
		try {
			underTest.key(1)
		} catch(UnsupportedOperationException ex) {			
		}
	}
	
	void testExtractGroovyProperty() {
		def extractor = underTest.p('name')
		assertEquals(GroovyPropertyValueExtractor, extractor.class)
		assertEquals('name', extractor.groovyPropertyExtracted)
	}
	
	private void assertMethodNameIs(expectedName, filter) {
		assert filter.valueExtractor instanceof ReflectionExtractor
		assert filter.valueExtractor.methodName == expectedName
	}
}
